﻿using Microsoft.Owin.Hosting;
using Newtonsoft.Json;
using System;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace AisinGioro.Server
{
    using Core;
    using Core.DTOs;
    using Hubs;

    public class ServerManager
    {        
        private string _hostUrl;
        public string HostUrl { get { return _hostUrl; } }

        public event EventHandler OnStartSucceed;
        public event EventHandler OnStopSucceed;
        public event EventHandler<ErrorOccuredEventArgs> OnFailed;

        CancellationTokenSource cancelTokenSource = null;
        
        public ServerManager(string hostUrl)
        {            
            _hostUrl = hostUrl;
        }

        public void Start()
        {
            cancelTokenSource = new CancellationTokenSource();
            Task.Factory.StartNew(DoWork, cancelTokenSource.Token);
        }

        /// <summary>
        /// Web根目录
        /// </summary>
        public static string WebRootDirectory
        {
            get
            {
                string webRootDir = ConfigurationManager.AppSettings["WebRootDirectory"];
                if (string.IsNullOrEmpty(webRootDir))
                    webRootDir = AppDomain.CurrentDomain.BaseDirectory + "wwwroot\\";

                if (!Directory.Exists(webRootDir))
                    throw new DirectoryNotFoundException("The root directory of the website does not exist.");

                return webRootDir;
            }
        }

        public void PollingMonitors(int minInterval = 15000, int maxInterval = 20000)
        {
            string filePath = WebRootDirectory + "\\data\\monitor.json";
            if (!File.Exists(filePath))
            {
                LogHelper.Error("The data source file does not exists. {0}", filePath);
                return;
            }
            string monitorStr = File.ReadAllText(filePath, Encoding.UTF8);
            MonitorRequestDto monitorDto = JsonConvert.DeserializeObject<MonitorRequestDto>(monitorStr);
            if (monitorDto == null || monitorDto.Items.Count == 0)
            {
                LogHelper.Warn("No monitor data {0}", filePath);
                return;
            }

            MsgHubCaller.Instance.MonitorFamily.Clear();
            //将所有监控请求项添加到列表
            MsgHubCaller.Instance.MonitorFamily.AddRange(monitorDto.Items.Select(i => i.Id));

            NetworkInfo ni = new NetworkInfo();
            string message;
            monitorDto.Items.Select(item => new Task(() =>
            {
                var responseDto = new MonitorResponseDto(item.Id);
                while (!cancelTokenSource.IsCancellationRequested)
                {
                    if (item.Type.ToLower() == "ping")
                    {
                        responseDto.IsOk = ni.Ping(item.Dest, out message);
                        responseDto.Text = message;
                    }
                    else if (item.Type.ToLower() == "port")
                    {
                        var spidx = item.Dest.IndexOf(':');
                        var dest = item.Dest.Substring(0, spidx);
                        var port = Int32.Parse(item.Dest.Substring(spidx + 1));
                        responseDto.IsOk = ni.SocketConnect(dest, port, out message);
                        responseDto.Text = message;
                    }
                    else if (item.Type.ToLower() == "http")
                    {
                        responseDto.IsOk = ni.HttpGet(item.Dest, out message);
                        responseDto.Text = message;
                    }
                    else
                    {
                        LogHelper.Error("Invalid monitor type {0}/{1}", item.Id, item.Type);
                        responseDto.IsOk = false;
                        responseDto.Text = $"Invalid monitor type '{item.Type}'";                        
                    }                    

                    SendMonitorResult(item, responseDto);  //发送检测结果
                    var delay = new Random(DateTime.Now.Millisecond).Next(minInterval, maxInterval);
                    Thread.Sleep(delay);  //随机延迟15-20秒
                }
            }, cancelTokenSource.Token)).ToList().ForEach(task =>
            {
                task.Start();
            });
        }

        public void PollingAliveAgents(int interval = 2000)
        {
            Task.Run(() =>
            {
                while (!cancelTokenSource.IsCancellationRequested)
                {
                    //超过10秒没有交互，认为掉线
                    MsgHubCaller.Instance.AliveAgentFamily.ForEach(i =>
                    i.LastSignedIn.AddSeconds(10) < DateTime.Now &&
                    i.Key.StartsWith("Hbt_"), item =>
                    {
                        MsgHubCaller.Instance.AliveAgentFamily.SendNotifyAsync(item.Key, $"[{item.Key.Substring(4)}][Heartbeat]_Down");
                    });
                    Thread.Sleep(interval);
                }
            }, cancelTokenSource.Token);
        }

        private void DoWork()
        {
            try
            {
                //创建WEB服务器监听
                using (WebApp.Start<OwinStartup>(_hostUrl))
                {
                    LogHelper.Trace("Server started! {0}", _hostUrl);

                    if (this.OnStartSucceed != null)
                        this.OnStartSucceed(this, EventArgs.Empty);

                    while (!cancelTokenSource.IsCancellationRequested)
                        Thread.Sleep(10);
                }

                LogHelper.Trace("Server stopped!");

                if (this.OnStopSucceed != null)
                    this.OnStopSucceed(this, EventArgs.Empty);  //Server stopped!
            }
            catch (Exception ex)
            {
                ex = ex.GetBaseException();
                LogHelper.Error(ex.Message + " " + _hostUrl);

                if (this.OnFailed != null)
                    this.OnFailed(this, new ErrorOccuredEventArgs(ex));
            }
        }

        public void Stop()
        {
            if (cancelTokenSource != null)
            {
                if (!cancelTokenSource.IsCancellationRequested)
                    cancelTokenSource.Cancel();
            }
        }

        private static void SendMonitorResult(MonitorRequestItem item, MonitorResponseDto dto)
        {
            if (!dto.IsOk)
                LogHelper.Error("PushMonitor: [{0}][{1}] {2}", item.Type, item.Dest, dto.Text);
            else
                LogHelper.Info("PushMonitor: {0}", JsonConvert.SerializeObject(dto));

            MsgHubCaller.Instance.Clients.Group(MsgHub.RoleName_Portal).receiveMonitor(dto); //推送给所有监控端
            if (!dto.IsOk)
                MsgHubCaller.Instance.MonitorFamily.SendNotifyAsync(item.Id, $"[{item.Type}][{item.Dest}]_{dto.Text}");  //异常通知
        }
    }
}
